package cn.tedu.mall.order.service.impl;


import cn.tedu.mall.common.exception.CoolSharkServiceException;
import cn.tedu.mall.common.pojo.domain.CsmallAuthenticationInfo;
import cn.tedu.mall.common.restful.JsonPage;
import cn.tedu.mall.common.restful.ResponseCode;
import cn.tedu.mall.order.mapper.OmsOrderItemMapper;
import cn.tedu.mall.order.mapper.OmsOrderMapper;
import cn.tedu.mall.order.service.IOmsCartService;
import cn.tedu.mall.order.service.IOmsOrderService;
import cn.tedu.mall.order.utils.IdGeneratorUtils;
import cn.tedu.mall.pojo.order.dto.OrderAddDTO;
import cn.tedu.mall.pojo.order.dto.OrderItemAddDTO;
import cn.tedu.mall.pojo.order.dto.OrderListTimeDTO;
import cn.tedu.mall.pojo.order.dto.OrderStateUpdateDTO;
import cn.tedu.mall.pojo.order.model.OmsCart;
import cn.tedu.mall.pojo.order.model.OmsOrder;
import cn.tedu.mall.pojo.order.model.OmsOrderItem;
import cn.tedu.mall.pojo.order.vo.OrderAddVO;
import cn.tedu.mall.pojo.order.vo.OrderDetailVO;
import cn.tedu.mall.pojo.order.vo.OrderListVO;
import cn.tedu.mall.product.service.order.IForOrderSkuService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@DubboService
@Service
@Slf4j
public class OmsOrderServiceImpl implements IOmsOrderService {

    //减库存
    @DubboReference
    private IForOrderSkuService dubooSkuService;

    //订单
    @Autowired
    private OmsOrderMapper orderMapper;

    //订单商品
    @Autowired
    private OmsOrderItemMapper orderItemMapper;

    //购物车
    @Autowired
    private IOmsCartService cartService;

    //生成订单的方法为分布式事务
    @GlobalTransactional
    @Override
    public OrderAddVO addOrder(OrderAddDTO orderAddDTO) {

        //首先先实例化能够新增到数据库的OmsOrder类
        OmsOrder omsOrder = new OmsOrder();
        // 业务逻辑层参数OrderAddDTO 的数据有限 其他OmsOrder需要的数据自己收集
        BeanUtils.copyProperties(orderAddDTO,omsOrder);
        //不足的数据 OmsOrder 单独编写一个方法
        loadOrder(omsOrder);
        //获得当前订单中所有商品的sku
        List<OrderItemAddDTO> orderItemAddDTOs = orderAddDTO.getOrderItems();
        if (orderItemAddDTOs==null||orderItemAddDTOs.isEmpty()) {
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"没有购物车商品");
        }
        // 上面集合泛型为OrderItemAddDTO,但新增到数据库的实体类OmsOrderItem
        // 所以实例化一个OmsOrderItem泛型集合,以备使用
        List<OmsOrderItem> omsOrderItems = new ArrayList<>();
        //遍历当前订单用包含所有商品的集合
        for (OmsOrderItem orderItem : omsOrderItems) {
            //
            //
            OmsOrderItem omsOrderItem = new OmsOrderItem();
            //
            BeanUtils.copyProperties(orderItem,omsOrderItem);
            //
            loadOrderItem(omsOrderItem);
            //
            omsOrderItem.setOrderId(omsOrder.getId());
            //
            omsOrderItems.add(omsOrderItem);
            //
            //
            Long skuId = omsOrderItem.getSkuId();
            //
            Integer quantity = omsOrderItem.getQuantity();
            //
            int result = dubooSkuService.reduceStockNum(skuId,quantity);
            //
            if (result == 0){
                log.warn("当前商品:{},库存不足!",skuId);
                // 抛出异常
                throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"库存不足");
            }
            //删除购物车信息
            OmsCart omsCart = new OmsCart();
            omsCart.setSkuId(skuId);
            omsCart.setUserId(omsOrder.getUserId());
            cartService.removeUserCarts(omsCart);
        }
        //执行将所有订单中商品新增到oms_order_item
        orderItemMapper.insertOrderItems(omsOrderItems);
        //
        orderMapper.insertOrder(omsOrder);
        //
        //
        //
        OrderAddVO addVO = new OrderAddVO();
        addVO.setId(omsOrder.getId());
        addVO.setSn(omsOrder.getSn());
        addVO.setCreateTime(omsOrder.getGmtCreate());
        addVO.setPayAmount(omsOrder.getAmountOfActualPay());
        //
        return addVO;
    }

    private void loadOrderItem(OmsOrderItem omsOrderItem) {
        //
        if (omsOrderItem.getId() == null) {
            Long id = IdGeneratorUtils.getDistributeId("order_item");
            omsOrderItem.setId(id);
        }
        //
        if (omsOrderItem.getSkuId() == null) {
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"订单中商品不能缺失SKU");
        }
        //


    }

    //生成的补充不足数据的方法  OmsOrder
    private void loadOrder(OmsOrder omsOrder) {
        //判断出id是否为空
        if (omsOrder.getId() == null) {
            //从leaf中获得分布式id
            Long id = IdGeneratorUtils.getDistributeId("order");
            omsOrder.setId(id);
        }
        //判断userId并为其赋值
        if (omsOrder.getUserId() == null) {
            Long userId = getUserId();
            omsOrder.setUserId(userId);
        }
        //判断sn是否为空并赋值
        if (omsOrder.getSn() == null) {
            omsOrder.setSn(UUID.randomUUID().toString());
        }
        //判断state并赋值
        if (omsOrder.getState() == null) {
            //state : 0 表示订单未支付,新创建的订单都是未支付的
            omsOrder.setState(0);
        }
        //订单生成时,订单创建时间,数据创建时间和最后修改时间,是同一时刻
        if (omsOrder.getGmtCreate() == null) {
            omsOrder.setGmtCreate(LocalDateTime.now());
        }
        if (omsOrder.getGmtOrder() == null) {
            omsOrder.setGmtOrder(omsOrder.getGmtCreate());
        }
        if (omsOrder.getGmtModified() == null) {
            omsOrder.setGmtModified(omsOrder.getGmtCreate());
        }

        //下面是金额的判断和计算
        //基本逻辑是原始金额加运费件优惠计算出最终金额
        if (omsOrder.getAmountOfDiscount() == null) {
            //为了防止计算时发生浮点偏移的误差,我们使用BigDecimal类型
            omsOrder.setAmountOfDiscount(new BigDecimal(0.0));
        }
        //运费的判断
        if (omsOrder.getAmountOfFreight() == null) {
            omsOrder.setAmountOfFreight(new BigDecimal(0.0));
        }
        //计算订单的金额
        if (omsOrder.getAmountOfOriginalPrice() == null) {
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"请确定购买商品的原始金额");

        }
        //判断实际支付金额
        if (omsOrder.getAmountOfActualPay() == null){
            // 实际金额=原始金额+运费-优惠
            // 获得各项金额
            BigDecimal originalPrice = omsOrder.getAmountOfOriginalPrice();
            BigDecimal discount = omsOrder.getAmountOfDiscount();
            BigDecimal freight = omsOrder.getAmountOfFreight();
            BigDecimal actualPay = originalPrice.add(freight).subtract(discount);
            omsOrder.setAmountOfActualPay(actualPay);

        }
    }

    @Override
    public void updateOrderState(OrderStateUpdateDTO orderStateUpdateDTO) {
        //
        OmsOrder omsOrder = new OmsOrder();
        BeanUtils.copyProperties(orderStateUpdateDTO,omsOrder);
        //
        orderMapper.updateOrderById(omsOrder);

    }

    //查询当前用户在指定时间范围内(默认一个月)所有订单
    //每个订单对象还包含订单中商品信息对象集合
    @Override
    public JsonPage<OrderListVO> listOrdersBetweenTimes(OrderListTimeDTO orderListTimeDTO) {
        // 本业务逻辑中比较特殊的是时间的判断,单独编写一个方法来效验时间或添加默认时间
        validTimesAndLoadTimes(orderListTimeDTO);
        //
        Long userId = getUserId();
        //
        orderListTimeDTO.setUserId(userId);
        //开启分页查询
        //
        PageHelper.startPage(orderListTimeDTO.getPage(), orderListTimeDTO.getPageSize());
        List<OrderListVO> orderListVOS = orderMapper.selectOrdersBetweenTimes(orderListTimeDTO);
        //
        return JsonPage.restPage(new PageInfo<>(orderListVOS));
    }

    private void validTimesAndLoadTimes(OrderListTimeDTO orderListTimeDTO) {
        //先取出起始时间和结束时间对象
        LocalDateTime start = orderListTimeDTO.getStartTime();
        LocalDateTime end = orderListTimeDTO.getEndTime();
        //起始时间 结束时间 任何一个为空,默认查询一个月订单
        if (start==null||end==null){
            //起始时间定一个月之前
            start = LocalDateTime.now().minusMonths(1);
            //结束时间是现在
            end = LocalDateTime.now();
            //将修改好的起始时间和解书时间赋值给orderListTimeDTO
            orderListTimeDTO.setStartTime(start);
            orderListTimeDTO.setEndTime(end);
        }else {
            //如果起始时间和结束时间都有值,判断结束时间要小于起始时间,抛异常
            if(end.toInstant(ZoneOffset.of("+8")).toEpochMilli()<start.toInstant(ZoneOffset.of("+8")).toEpochMilli()){
                //发生异常,程序终止给出提示
                throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"查询结束时间应大于起始时间");
            }

        }

    }

    @Override
    public OrderDetailVO getOrderDetail(Long id) {
        return null;
    }

    // 业务逻辑层中可能有多个方法需要获得当前用户信息
    // 我们可以定义一个方法实现从SpringSecurity中获得用户信息
    public CsmallAuthenticationInfo getUserInfo(){
        // 获得authenticationToken对象
        UsernamePasswordAuthenticationToken authenticationToken
                = (UsernamePasswordAuthenticationToken) SecurityContextHolder
                .getContext().getAuthentication();

        //判断获取的对象是不是空
        if (authenticationToken != null) {
            CsmallAuthenticationInfo csmallAuthenticationInfo = (CsmallAuthenticationInfo) authenticationToken.getCredentials();

        }

        throw new CoolSharkServiceException(ResponseCode.UNAUTHORIZED,"没有登录信息");

    }

    //单独获得当前登录用户的id
    public Long getUserId(){
        return getUserInfo().getId();
    }

}
